package webwx

import (
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"math/rand"
	"os"
	"strings"
	"sync"
	"time"
)

type WebWXMsg struct {
	XMLName     xml.Name `xml:"error"`
	Ret         int      `xml:"ret"`
	Message     string   `xml:"message"`
	Skey        string   `xml:"skey"`
	WXsid       string   `xml:"wxsid"`
	WXuin       int64    `xml:"wxuin"`
	PassTicket  string   `xml:"pass_ticket"`
	IsGrayScale int      `xml:"isgrayscale"`
}

type WebWXContext struct {
	sLoginUUID  string
	qrCodeData  []byte
	userAvatar  []byte
	redirectURI string
	webxmmsg    WebWXMsg
}

var conns map[string]*tls.Conn //微信连接connection
//var conn *tls.Conn             //微信连接connection
var err error //错误
var wg sync.WaitGroup
var mutex sync.Mutex
var tip int

func init() {
	rand.Seed(time.Now().UnixNano())
	tip = 1
	conns = make(map[string]*tls.Conn)
}

func deviceID() string {
	x := rand.Int63n(899999999999999) + 100000000000000
	return fmt.Sprintf("e%d", x)
}

func checkerror(err error) {
	if err != nil {
		panic(err)
	}
}

func readFully(conn *tls.Conn) ([]byte, error) {
	result := bytes.NewBuffer(nil)
	var buf [512]byte
	for {
		n, err := conn.Read(buf[0:])
		result.Write(buf[0:n])
		if err != nil {
			if err == io.EOF {
				break
			}
			return nil, err
		} else if n < 512 {
			break
		}
	}
	return result.Bytes(), nil
}

func (this *WebWXContext) InitConn() {
	conf := &tls.Config{}
	conf.InsecureSkipVerify = true

	connLogin, err := tls.Dial("tcp", "101.227.160.102:443", conf)
	checkerror(err)
	conns["https://login.wx.qq.com"] = connLogin

	connWx, err := tls.Dial("tcp", "101.226.76.164:443", conf)
	checkerror(err)
	conns["https://login.weixin.qq.com"] = connWx
	conns["https://wx.qq.com"] = connWx
}

func (this *WebWXContext) CloseConn() {
	//	if conn != nil {
	//		conn.Close()
	//	}
	if conns != nil {
		for _, conn := range conns {
			conn.Close()
		}
	}
}

func getWebGETResult(url string) []byte {
	fmt.Println("GET URL---", url)
	urltmp := url[8:]
	fmt.Println(urltmp)
	urlarray := strings.Split(urltmp, "/")
	urlhost := urlarray[0]
	conn := conns["https://"+urlhost]

	var httpContent string
	httpContent += "GET "
	httpContent += url
	httpContent += " HTTP/1.1\r\n"
	httpContent += "Host:"
	httpContent += urlhost
	httpContent += "\r\n"
	//	httpContent += "Host:login.wx.qq.com\r\n"
	httpContent += "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n"
	httpContent += "Accept-Encoding: gzip, deflate, br\r\n"
	httpContent += "Connection: keep-alive\r\n\r\n"
	var htmlContent []byte
	func() {
		mutex.Lock()
		defer mutex.Unlock()
		_, err = conn.Write([]byte(httpContent))
		checkerror(err)
		htmlContent, err = readFully(conn)
		checkerror(err)
	}()
	htmlContentBodyindex := strings.Index(string(htmlContent), "\r\n\r\n") + 4
	fmt.Println("Resp---", string(htmlContent[htmlContentBodyindex:]))
	return htmlContent[htmlContentBodyindex:]
}

func getWebPOSTResult(url, postBody string) []byte {
	fmt.Println("POST URL---", url, "---POST Body---", postBody)
	urltmp := url[8:]
	fmt.Println(urltmp)
	urlarray := strings.Split(urltmp, "/")
	urlhost := urlarray[0]
	conn := conns["https://"+urlhost]

	var httpContent string
	httpContent += "POST "
	httpContent += url
	httpContent += " HTTP/1.1\r\n"
	httpContent += "Host:"
	httpContent += urlhost
	httpContent += "\r\n"
	//	httpContent += "Host:wx.qq.com\r\n"
	httpContent += "Accept: application/json,text/plain,*/*\r\n"
	httpContent += "Accept-Encoding: gzip, deflate, br\r\n"
	httpContent += "Content-Type:application/json;charset=utf-8\r\n"
	httpContent += "Connection: keep-alive\r\n"
	httpContent += "Pragma: no-cache\r\n"
	httpContent += "Cache-Control: no-cache\r\n"
	httpContent += postBody
	httpContent += "\r\n\r\n"
	var htmlContent []byte
	func() {
		mutex.Lock()
		defer mutex.Unlock()
		_, err = conn.Write([]byte(httpContent))
		checkerror(err)
		htmlContent, err = readFully(conn)
		checkerror(err)
	}()
	htmlContentBodyindex := strings.Index(string(htmlContent), "\r\n\r\n") + 4
	fmt.Println("Resp---", string(htmlContent[htmlContentBodyindex:]))
	return htmlContent[htmlContentBodyindex:]
}

func (this *WebWXContext) GetQRcodeUUID() {
	var qrcodeURLtpl = "https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage&fun=new&lang=zh_CN&_=%d"
	htmlContentBody := string(getWebGETResult(fmt.Sprintf(qrcodeURLtpl, time.Now().UnixNano()/1000000)))
	var nLoginCode int
	fmt.Sscanf(htmlContentBody, "window.QRLogin.code = %d; window.QRLogin.uuid = \"%12s\";", &nLoginCode, &this.sLoginUUID)
	fmt.Println(nLoginCode, this.sLoginUUID)
	if nLoginCode != 200 {
		err = errors.New("window.QRLogin.code != 200")
		checkerror(err)
	}
}

func (this *WebWXContext) PostWebWXstatreport() bool {
	url := "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatreport?fun=new"
	bodyTpl := `{"BaseRequest":{"Uin":"","Sid":"","DeviceID":"%s"},"Count":2,"List":[{"Type":1,"Text":"{\"type\":\"[app-runtime]\",\"data\":{\"unload\":{\"listenerCount\":117,\"watchersCount\":115,\"scopesCount\":30}}}"},{"Type":1,"Text":"{\"type\":\"[app-timing]\",\"data\":{\"appTiming\":{\"qrcodeStart\":%d,\"qrcodeEnd\":%d},\"pageTiming\":{\"navigationStart\":%d,\"unloadEventStart\":%d,\"unloadEventEnd\":%d,\"redirectStart\":0,\"redirectEnd\":0,\"fetchStart\":%d,\"domainLookupStart\":%d,\"domainLookupEnd\":%d,\"connectStart\":%d,\"connectEnd\":%d,\"secureConnectionStart\":%d,\"requestStart\":%d,\"responseStart\":%d,\"responseEnd\":%d,\"domLoading\":%d,\"domInteractive\":%d,\"domContentLoadedEventStart\":%d,\"domContentLoadedEventEnd\":%d,\"domComplete\":%d,\"loadEventStart\":%d,\"loadEventEnd\":%d,\"timeToNonBlankPaint\":%d}}}"}]}`
	timed := func() int64 {
		return time.Now().Unix()
	}
	httpContent := fmt.Sprintf(bodyTpl, deviceID(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed(), timed())
	/*htmlContentBody*/ _ = getWebPOSTResult(url, httpContent)
	return true
}

func (this *WebWXContext) GetQRcodeImg() bool {
	url := "https://login.weixin.qq.com/qrcode/"
	url += this.sLoginUUID
	this.qrCodeData = getWebGETResult(url)
	return true
}

func (this *WebWXContext) GetUserAvatar() bool {
	var loginCode int
	for loginCode != 201 {
		urltpl := "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=%s&tip=%d&r=-412638148&_=%d"
		url := fmt.Sprintf(urltpl, this.sLoginUUID, tip, time.Now().UnixNano()/1000000)
		if tip == 1 {
			tip = 0
		}
		htmlContentBody := string(getWebGETResult(url))
		fmt.Sscanf(htmlContentBody, "window.code=%d;", &loginCode)
		if loginCode == 201 {
			nIndex := strings.Index(htmlContentBody, "data:img/jpg;base64,") + 20
			userAvatarBase64 := htmlContentBody[nIndex : len(htmlContentBody)-2]
			this.userAvatar, err = base64.StdEncoding.DecodeString(userAvatarBase64)
			checkerror(err)

			avatarFile, err := os.Create("useravatar.jpg")
			checkerror(err)
			defer avatarFile.Close()
			avatarFile.Write(this.userAvatar)
		}
	}
	return true
}

func (this *WebWXContext) GetRedirectURI() bool {
	var loginCode int
	for loginCode != 200 {
		urltpl := "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=%s&tip=0&r=-412638148&_=%d"
		url := fmt.Sprintf(urltpl, this.sLoginUUID, time.Now().UnixNano()/1000000)
		htmlContentBody := string(getWebGETResult(url))
		fmt.Sscanf(htmlContentBody, "window.code=%d;", &loginCode)
		if loginCode == 200 {
			nIndex := strings.Index(htmlContentBody, "window.redirect_uri=\"") + 21
			this.redirectURI = htmlContentBody[nIndex : len(htmlContentBody)-2]
			fmt.Println(this.redirectURI)
		}
	}
	return true
}

func (this *WebWXContext) GetWebWXMsg() bool {
	if len(this.redirectURI) != 0 {
		htmlContentBody := string(getWebGETResult(this.redirectURI))
		err = xml.Unmarshal([]byte(htmlContentBody), &this.webxmmsg)
		fmt.Println(this.webxmmsg)
		return true
	}
	return false
}

func (this *WebWXContext) PostWebWXinit() bool {
	var postBodyTpl = `{"BaseRequest":{"Uin":"%d","Sid":"%s","Skey":"%s","DeviceID":"%s"}}`
	postBody := fmt.Sprintf(postBodyTpl, this.webxmmsg.WXuin, this.webxmmsg.WXsid, this.webxmmsg.Skey, deviceID())
	var postUrlTpl = `https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=%d0&lang=zh_CN&pass_ticket=%s`
	postUrl := fmt.Sprintf(postUrlTpl, time.Now().Unix(), this.webxmmsg.PassTicket)
	/*htmlContentBody*/ _ = getWebPOSTResult(postUrl, postBody)
	return true
}

func (this *WebWXContext) GetWebWXContact() bool {
	var urlTpl = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%d&seq=0&skey=%s"
	url := fmt.Sprintf(urlTpl, this.webxmmsg.PassTicket, time.Now().Unix(), this.webxmmsg.Skey)
	/*htmlContentBody*/ _ = getWebGETResult(url)
	return true
}
